构建准备:Prisma 多 Clients 打包脚本
在 NestJS 服务端项目进行打包部署之前,如果项目是一个多租户(multi-tenant)架构,Prisma 的数据库类型(provider)可能是写死在 schema.prisma 中的。然而实际生产环境中,我们需要根据 .env 配置文件中的数据库类型来动态生成对应的 Prisma Client。本节将构建一个预构建脚本,实现根据环境变量自动生成多个数据库的 Prisma Client。
问题分析
多租户场景下 Prisma 打包面临的核心问题:
| 问题 | 说明 |
|---|---|
| provider 写死 | schema.prisma 中的 datasource 只能指定一种数据库 |
| 多数据库需求 | 实际可能需要同时支持 PostgreSQL、MySQL 等多种数据库 |
| 部署一致性 | Docker 镜像中必须包含所有需要的 Prisma Client |
实现思路
整体流程分为以下几步:
- 读取
.env配置文件,解析租户模式与数据库类型 - 校验数据库类型的合法性
- 使用正则替换
schema.prisma中的 provider - 循环执行
npx prisma generate生成多个 Client
创建构建脚本
在项目根目录下创建 scripts/build.mjs 文件:
// scripts/build.mjs
import dotenv from 'dotenv-flow'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import { execSync } from 'child_process'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// Step 1: 读取环境配置文件
const envFiles = dotenv.listDotenvFiles(__dirname)
const parsedConfig = dotenv.parse(envFiles)
console.log('Parsed config:', parsedConfig)
// Step 2: 获取租户模式与数据库类型
const tenantMode = parsedConfig.tenantMode
const tenantDBType = parsedConfig.tenantDBType
const dbArr = tenantDBType ? tenantDBType.split(',') : []
// Step 3: 校验数据库类型
const validDBTypes = ['postgresql', 'mysql', 'sqlite', 'sqlserver', 'mongodb', 'cockroachdb']
const isValid = dbArr.every((db) => validDBTypes.includes(db))
if (!isValid) {
throw new Error(`Invalid database type in tenantDBType: ${tenantDBType}`)
}
// Step 4: 读取 schema.prisma 文件
const prismaFile = path.join(__dirname, '..', 'prisma', 'schema.prisma')
let schemaContent = fs.readFileSync(prismaFile, 'utf8')
// Step 5: 读取 .env 中指定的 prisma 数据库类型
const types = parsedConfig.prismaDBType
const typesArr = types ? types.split(',') : []
if (types && typesArr.length > 0) {
for (const type of typesArr) {
// 使用正则替换 provider
const regex = /provider\s*=\s*"(postgresql|mysql|sqlite|sqlserver|mongodb|cockroachdb)"/g
const updatedSchema = schemaContent.replace(regex, `provider = "${type}"`)
// 写入临时 schema 文件
fs.writeFileSync(prismaFile, updatedSchema, 'utf8')
// 同步执行 prisma generate(阻塞循环,确保顺序执行)
const result = execSync('npx prisma generate', {
cwd: path.join(__dirname, '..'),
})
console.log(`type: ${type}`, result.toString())
}
}
javascript
关键技术点
1. ES Module 中的 __dirname 处理
在 ES Module 规范中没有 __dirname 全局变量,需要手动转换:
import { fileURLToPath } from 'url'
import path from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
javascript
2. execSync 替代 exec 保证顺序执行
child_process.exec 是异步方法,在 for 循环中会导致多次执行并行发起,产生不可预期的结果。使用 execSync 可以阻塞循环,确保每个 Client 按顺序生成:
import { execSync } from 'child_process'
// 同步执行,会阻塞直到命令完成
const output = execSync('npx prisma generate', {
cwd: path.join(__dirname, '..'), // 指定项目根目录作为工作目录
})
javascript
3. 正则替换 provider
// 匹配 schema.prisma 中的 provider 声明并替换
const regex = /provider\s*=\s*"(postgresql|mysql|sqlite|sqlserver|mongodb|cockroachdb)"/g
const updatedSchema = schemaContent.replace(regex, `provider = "${newType}"`)
javascript
4. Prisma Schema 配置
schema.prisma 中支持的所有数据库类型如下:
datasource db {
provider = "postgresql" // 可选: mysql, sqlite, sqlserver, mongodb, cockroachdb
}
prisma
.env 配置示例
# 租户模式
tenantMode=true
# 租户数据库类型(逗号分隔)
tenantDBType=postgresql,mysql
# Prisma 需要生成的数据库类型
prismaDBType=postgresql,mysql
env
验证结果
执行脚本后,检查 Prisma Client 是否正确生成:
node scripts/build.mjs
bash
预期输出:
type: postgresql
Prisma Client postgresql generated successfully
type: mysql
Prisma Client mysql generated successfully
text
在 prisma/clients/ 目录下可以确认:
clients/postgresql/index.js包含 PostgreSQL 的 Clientclients/mysql/index.js包含 MySQL 的 Client
通过搜索新生成的 model(如 BalanceRecord)来验证 schema 是否为最新版本。
版本更新策略
在升级 @swc/core 等依赖时,建议遵循以下策略:
| 版本号类型 | 更新策略 | 风险等级 |
|---|---|---|
| patch(x.x.1 -> x.x.2) | 直接更新 | 低 |
| minor(x.1.x -> x.2.x) | 更新后简单测试 | 中 |
| major(1.x.x -> 2.x.x) | 测试并提交后再更新 | 高 |
# 使用 ncu 检查可更新版本
npx ncu -u
# 分步更新:先更新 patch 和 minor,测试通过提交后再更新 major
bash
小结
本节构建了一个预构建脚本,解决了多租户场景下 Prisma 多数据库 Client 的生成问题。核心要点:
- 通过
dotenv-flow读取环境配置,获取数据库类型列表 - 使用正则替换
schema.prisma中的 provider,循环生成多种数据库的 Client - 使用
execSync保证命令的顺序执行,避免异步并发问题 - 对数据库类型进行合法性校验,防止配置错误
后续在完成 Docker 镜像配置后,会将这些 Prisma Client 打包到镜像中。
↑